using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics.Contracts;
using Konst3d.Efficiency.Suite;

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    internal class TaskStorage: ITaskCollection, ITaskStorage, IDisposable
    {
        #region Types

        public delegate void ItemAddedOrRemoved(TaskStorage sender, int id);

        #endregion // Types

        #region Constants

        private const string TASK_FILE_EXT = ".task";

        #endregion // Constants

        #region Events

        public event ItemAddedOrRemoved TaskAdded;
        public event ItemAddedOrRemoved TaskRemoved;

        #endregion // Events

        #region Create/Load/Delete Methods

        public static TaskStorage Create(string fileName, bool overwrite, ICryptographicEngine cryptographicEngine) // file name w/o ext
        {
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            TaskFile file = null;
            LabelsStorage labels = null;

            try
            {
                file = TaskFile.Create(fileName + TASK_FILE_EXT, overwrite);
                labels = LabelsStorage.Create(fileName, overwrite, cryptographicEngine);

                return new TaskStorage(file, labels);
            }
            catch
            {
                if (file != null)
                {
                    file.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public static TaskStorage Open(string fileName, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            TaskFile file = null;
            LabelsStorage labels = null;

            try
            {
                file = TaskFile.Open(fileName + TASK_FILE_EXT);
                labels = LabelsStorage.Open(fileName, cryptographicEngine);

                return new TaskStorage(file, labels);
            }
            catch
            {
                if (file != null)
                {
                    file.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public void Delete()
        {
            Contract.Assert(!_disposed);

            _file.Delete();
            _labels.Delete();

            Dispose();
        }

        #endregion // Create/Load/Delete Methods

        #region Constructor/Finalizer

        private TaskStorage(TaskFile file, LabelsStorage labels)
        {
            Contract.Requires(file != null);
            Contract.Requires(labels != null);

            _file = file;
            _labels = labels;

            var entries = _file.GetAll();

            foreach(var entry in entries)
            {
                var task = new Task(this,
                                    entry.Key,
                                    _labels.GetLabel(entry.Value.TitleID),
                                    _labels.GetLabel(entry.Value.DescrID),
                                    (TaskSeverity)entry.Value.Severity,
                                    (TaskState)entry.Value.State,
                                    entry.Value.Created,
                                    entry.Value.DeadLine);

				_tasks.Add(entry.Key, task);

                if (task.State == TaskState.Active)
                {
                    if (task.DeadLine < DateTime.Now)
                    {
                        task.State = TaskState.Failed;
                    }
                }
            }
        }

        ~TaskStorage()
        {
            Dispose(false);
        }

        #endregion // Constructor/Finalizer

        #region ITaskCollection Members

		public int Count
		{
			get
			{
				return _tasks.Count;
			}
		}

        public ITask this[int id]
        {
            get
            {
                return _tasks[id];
            }
        }

        public int Add(DateTime created, DateTime deadLine, TaskSeverity severity, string title, string description)
        {
            Contract.Assert(deadLine > created);

            int titleID = _labels.AddLabel(title);
            int descrID = _labels.AddLabel(description);

            int id   = _file.AddEntry(titleID, descrID, (byte)severity, (byte)TaskState.Active, created, deadLine);
            var task = new Task(this, id, title, description, severity, TaskState.Active, DateTime.Now, deadLine);

            _tasks.Add(id, task);

            if (TaskAdded != null)
            {
                TaskAdded(this, id);
            }

            return id;
        }

        public void Remove(int id)
        {
            Contract.Assert(_tasks.ContainsKey(id));

            // TOFIX: cache somehow these IDs...
            _labels.DeleteLabel(_file.GetEntryTitleID(id));
            _labels.DeleteLabel(_file.GetEntryDescrID(id));

            _tasks.Remove(id);

            if (TaskRemoved != null)
            {
                TaskRemoved(this, id);
            }

            _file.RemoveEntry(id);
        }

        public ICollection<ITask> Get(TaskState state)
        {
            // should we do any caching?

            List<ITask> list = new List<ITask>();

            foreach (var task in _tasks.Values)
            {
                if (task.State == state)
                {
                    list.Add(task);
                }
            }

            return list;
        }

        public ICollection<ITask> GetActiveTTL(TimeSpan timeToLive)
        {
            List<ITask> list = new List<ITask>();

            foreach (var task in _tasks.Values)
            {
                if (task.State == TaskState.Active &&
                    task.DeadLine - DateTime.Now <= timeToLive)
                {
                    list.Add(task);
                }
            }

            return list;
        }

        #endregion

        #region ITaskStorage Members

        void ITaskStorage.UpdateTitle(int id, string title)
        {
            Contract.Requires(id >= 0);
            Contract.Assert(_tasks.ContainsKey(id));

            int labelID = _file.GetEntryTitleID(id);
            int newLabelID = _labels.UpdateLabel(labelID, title);

            _file.SetEntryTitleID(id, newLabelID);
        }

        void ITaskStorage.UpdateDescription(int id, string description)
        {
            Contract.Requires(id >= 0);
            Contract.Assert(_tasks.ContainsKey(id));

            int labelID = _file.GetEntryDescrID(id);
            int newLabelID = _labels.UpdateLabel(labelID, description);

            _file.SetEntryDescrID(id, newLabelID);
        }

        void ITaskStorage.UpdateSeverity(int id, TaskSeverity severity)
        {
            Contract.Requires(id >= 0);
            Contract.Assert(_tasks.ContainsKey(id));

            _file.SetEntrySeverity(id, (byte)severity);
        }

        void ITaskStorage.UpdateState(int id, TaskState state)
        {
            Contract.Requires(id >= 0);
            Contract.Assert(_tasks.ContainsKey(id));

            _file.SetEntryState(id, (byte)state);
        }

        void ITaskStorage.UpdateDeadLine(int id, DateTime deadLine)
        {
            Contract.Requires(id >= 0);
            Contract.Assert(_tasks.ContainsKey(id));

            _file.SetEntryDeadLine(id, deadLine);
        }

        #endregion

        #region IEnumerable<ITask> Members

        public IEnumerator<ITask> GetEnumerator()
        {
            return _tasks.Values.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _tasks.GetEnumerator();
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _file.Dispose();
                    _file = null;

                    _labels.Dispose();
                    _labels = null;

                    _tasks.Clear();
                }

                _disposed = true;
            }
        }

        #endregion

        #region Fields

        private bool _disposed = false;
        private TaskFile _file;
        private LabelsStorage _labels;
        private Dictionary<int, Task> _tasks = new Dictionary<int, Task>();

        #endregion // Fields
    }
}
